﻿using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UtilsDraw
{
    public delegate void SetPixel(int x, int y, Color color);
    public static void DrawLine(SetPixel setPixel, Texture2D texture, Vector3 startPos, Vector3 endPos, Color color, int size = 1, float hardValue = 1)
    {
        var drawLine = endPos - startPos;
        var drawdir = drawLine.normalized;
        int lineM = 0;
        if (Mathf.Abs(drawLine.x) > Mathf.Abs(drawLine.y))
            lineM = (int)Mathf.Abs(drawLine.x) + 1;
        else
            lineM = (int)Mathf.Abs(drawLine.y) + 1;
        var radius = size / 2f;
        for (float i = 0; i < lineM; i += radius)
        {
            float scale = (lineM - i) / (float)lineM;
            var newY = Mathf.Clamp(startPos.y + (drawLine.y * scale), 0, texture.height);
            var newX = Mathf.Clamp(startPos.x + (drawLine.x * scale), 0, texture.width);
            if (size == 1)
                setPixel((int)newX, (int)newY, color);
            var horizAxis = Vector3.Cross(drawdir, Vector3.forward);
            horizAxis = horizAxis.normalized;


            DrawPoint(setPixel, texture, new Vector3(newX, newY, 0), color, size, hardValue);

            //for (int x=0; x< radius; ++x)
            //{
            //    for (int y = 0; y < radius; ++y)
            //    {
            //        setPixel((int)newX+x, (int)newY+y, color);
            //    }
            //}
            //for (int x = 1; x < radius; ++x)
            //{
            //    for (int y = 0; y < radius; ++y)
            //    {
            //        setPixel((int)newX-x, (int)newY-y, color);
            //    }
            //}


            //for (int j =0; j<radius; ++j)
            //{
            //    var offset = new Vector2Int(Mathf.RoundToInt(horizAxis.x * j), Mathf.RoundToInt(horizAxis.y * j));
            //    
            //    var newX2 = Mathf.Clamp(newX + offset.x, 0, texture.width);
            //    var newY2 = Mathf.Clamp(newY + offset.y, 0, texture.height);
            //    setPixel((int)newX2, (int)newY2, color);
            //    //if (Mathf.Abs(offset.x) == Mathf.Abs(offset.y) )
            //    //{
            //    //}
            //    setPixel((int)newX2, (int)newY2 + 1, color);
            //    setPixel((int)newX2, (int)newY2 - 1, color);
            //    //if (drawdir.y > 0)
            //    //if (drawdir.y < 0)
            //}
            //for (int j = 0; j < radius; ++j)
            //{
            //    var offset = new Vector2Int(Mathf.RoundToInt(horizAxis.x * j), Mathf.RoundToInt(horizAxis.y * j));
            //
            //    var newX2 = Mathf.Clamp(newX - offset.x, 0, texture.width);
            //    var newY2 = Mathf.Clamp(newY - offset.y, 0, texture.height);
            //    setPixel((int)newX2, (int)newY2, color);
            //    //if (Mathf.Abs(offset.x) + Mathf.Abs(offset.y) >= 2)
            //    //{
            //    //}
            //    //if (drawdir.y > 0)
            //        setPixel((int)newX2, (int)newY2 + 1, color);
            //    //if (drawdir.y < 0)
            //        setPixel((int)newX2, (int)newY2 - 1, color);
            //}
        }
    }
    public static void DrawPoint(SetPixel setPixel, Texture2D texture, Vector3 startPos, Color color, int size = 1, float hardValue = 1)
    {
        var radius = size / 2;
        var newY = Mathf.Clamp(startPos.y - radius, 0, texture.height);
        var newX = Mathf.Clamp(startPos.x - radius, 0, texture.width);

        var maxSize = size * 0.88f;
        for (int x = 0; x <= size; ++x)
        {
            for (int y = 0; y <= size; ++y)
            {
                var xx = (int)newX + x;
                var yy = (int)newY + y;
                var distance = Mathf.Abs(xx - startPos.x) + Mathf.Abs(yy - startPos.y);
                if (distance < maxSize)
                {
                    var realColor = color;
                    realColor.a = 1;
                    realColor.a = Mathf.Min(1f, (maxSize - distance) / maxSize * (hardValue * 0.5f + 0.5f) + hardValue);
                    if (hardValue != 1)
                    {
                        var oldColor = texture.GetPixel(xx, yy);
                        //var newRGB = new Vector3(realColor.r, realColor.g, realColor.b) * realColor.a + new Vector3(oldColor.r, oldColor.g, oldColor.b) * (oldColor.a - realColor.a);
                        //var newRGB = new Vector3(realColor.r * oldColor.r * 2f, realColor.g * oldColor.g * 2f, realColor.b * oldColor.b * 2f);
                        //realColor = new Color(newRGB.x, newRGB.y, newRGB.z,1f - (1f-realColor.a) * (1f-oldColor.a));
                        var alpha = 1f - (1f - realColor.a) * (1f - oldColor.a);
                        realColor = Color.Lerp(oldColor, realColor, realColor.a);
                        realColor.a = alpha;
                    }
                    realColor.a = realColor.a * color.a;
                    setPixel(xx, yy, realColor);
                }
            }
        }
    }

    public static float blend(float baseColor, float blendColor)
    {
        if (blendColor < 0.5f)
            return 2f * baseColor * blendColor;
        else
            return 1f - 2f * (1f - baseColor) * (1f - blendColor);
    }
    public static void DrawLine(Texture2D texture, Vector3 startPos, Vector3 endPos, Color color, int size = 1, float hardValue = 1)
    {
        DrawLine(texture.SetPixel, texture, startPos, endPos, color, size, hardValue);
    }
    public static void DrawCircle(Texture2D texture, Vector3 startPos, float radius, Color color, int accuracy = 50, int size = 1, float hardValue = 1)
    {
        DrawCircle(texture.SetPixel, texture, startPos, radius, color, accuracy, size, hardValue);
    }
    public static void DrawCircle(SetPixel setPixel, Texture2D texture, Vector3 startPos, float radius, Color color, int accuracy = 50, int size = 1, float hardValue = 1)
    {
        Vector3 previousPos = startPos + Vector3.up * radius;
        for (int i = 1; i <= accuracy; ++i)
        {
            var dir = UnityEngine.Quaternion.AngleAxis(i / (float)accuracy * 360f, Vector3.forward) * Vector3.up;
            var newPos = startPos + dir * radius;
            DrawLine(setPixel, texture, previousPos, newPos, color, size, hardValue);
            previousPos = newPos;
        }

    }
    public static Queue<Vector2Int> FullColor(SetPixel setPixel, Texture2D texture, int x, int y, Color fullColor, Color newColor)
    {
        if (IsEqupmentColor(fullColor, newColor))
            return null;
        Queue<Vector2Int> m_FullQueue = new Queue<Vector2Int>();
        m_FullQueue.Enqueue(new Vector2Int(x, y));
        int count = 0;
        while (m_FullQueue.Count > 0)
        {
            var tureData = m_FullQueue.Dequeue();
            var colorInTexture = texture.GetPixel(tureData.x, tureData.y);
            if (tureData.x > 0 && tureData.y > 0 &&
                tureData.x < texture.width && tureData.y < texture.height &&
                IsEqupmentColor(texture.GetPixel(tureData.x, tureData.y), fullColor))
            {
                setPixel(tureData.x, tureData.y, newColor);

                m_FullQueue.Enqueue(new Vector2Int(tureData.x + 1, tureData.y));
                m_FullQueue.Enqueue(new Vector2Int(tureData.x - 1, tureData.y));
                m_FullQueue.Enqueue(new Vector2Int(tureData.x, tureData.y + 1));
                m_FullQueue.Enqueue(new Vector2Int(tureData.x, tureData.y - 1));
            }
            count++;
        }
        return m_FullQueue;
    }
    /// <summary>
    /// make full for a area of texture
    /// </summary>
    /// <param name="texture">the texutre you want to full</param>
    /// <param name="x">the start point of full</param>
    /// <param name="y">the start point of full</param>
    /// <param name="fullColor">the color will be replaced by full operation</param>
    /// <param name="newColor">the new color will replace the oldColor</param>
    public static Queue<Vector2Int> FullColor(Texture2D texture, int x, int y, Color fullColor, Color newColor)
    {
        return FullColor(texture.SetPixel, texture, x, y, fullColor, newColor);
    }
    public static bool IsEqupmentColor(Color color1, Color color2)
    {
        if (Mathf.Abs(color1.r - color2.r) < 0.0039f &&
                Mathf.Abs(color1.g - color2.g) < 0.0039f &&
                Mathf.Abs(color1.b - color2.b) < 0.0039f &&
                Mathf.Abs(color1.a - color2.a) < 0.0039f)
        {
            return true;
        }
        return false;
    }


    static public GameObject DrawLine(GameObject parent, GameObject self, Vector2 pointA, Vector2 pointB)	//画线方法
    {
        if (!self)
            self = new GameObject("line");//
        Image image = self.GetComponent<Image>();
        if (!image)
            image = self.AddComponent<Image>();//
        image.color = new Color(0.8f, 0.65f, 0.3f);
        image.rectTransform.pivot = new Vector2(0, 0.5f);
        image.raycastTarget = false;
        self.transform.SetParent(parent.transform, false);       //将该图片设为graphContainer的子物体
                                                                 //就是在画板内画线
        RectTransform rectTransform = self.GetComponent<RectTransform>();	//获取 RectTransform 组件
        Vector2 dir = pointB - pointA;	//两点间的向量

        //同样将线段锚点设为画板左下角(原点)
        rectTransform.anchorMin = new Vector2(0, 0);
        rectTransform.anchorMax = new Vector2(0, 0);
        rectTransform.sizeDelta = new Vector2(dir.magnitude, 3f);	//线段的长宽，长为两点间向量的长度，就是两点间距离

        rectTransform.anchoredPosition = pointA + dir / 2;			//线段的中心点，为两点间的中心点
        float angle = RotateAngle(dir.x, dir.y);				   //线段的旋转角度
        rectTransform.localEulerAngles = new Vector3(0, 0, angle);	//旋转线段
        self.transform.position = pointA;
        return self;
    }
    static private float RotateAngle(float x, float y)	//旋转方法
    {
        float angle = Mathf.Atan2(y, x) * 180 / 3.14f;//Atan2返回的是弧度，需要乘以180/PI得到角度，这里PI直接用了3.14
        return angle;
    }
}